#include "kore2.h"

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
	 
/**
 * KoreApplication Constructor
 *
 * Will parse out some kore2-specific command line arguments that can tweak
 * the functionality somewhat.
 *
 *   --force-mmx    Force the use of MMX for some SDL operations
 *   --disable-mmx  Disable the use of MMX for the SDL operations that support it
 *
 * @param int argc passed to main
 * @param char** argv passed to pain
 */
kore::KoreApplication::KoreApplication(int argc, char** argv)
	: mp_primarycontext(NULL)
	, m_cursormode(CM_HARD)
{

	printf("Kore² Engine %d.%d\n", KORE2_MAJOR, KORE2_MINOR );

	// Manually pick out the relevant options, this is probably the fastest
	// way to do it since we dont need any advanced argument parsing.
	for( int n = 0; n < argc; n++ )
	{
		if (0 == strcmp(argv[n],"--force-mmx"))
		{
			printf("Forcing MMX\n");
		}
		if (0 == strcmp(argv[n],"--disable-mmx"))
		{
			printf("Disabling MMX\n");
		}
		if (0 == strcmp(argv[n],"--opengl"))
		{
			printf("Forcing OpenGL Mode\n");
		}
	}
	
	this->scenemanager = new kore::SceneManager();

}

kore::KoreApplication::~KoreApplication()
{
	if (this->mp_primarycontext)
	{
		printf("[application] Destroying orphaned primary context\n");
		this->mp_primarycontext->detachPrimaryContext();
		delete this->mp_primarycontext;	
	}
	// Close the SDL backend
	SDL_Quit();

	printf("[application] Clean shutdown.\n");
}

/**
 * Set the desired framerate
 *
 * @param long framerate The desired framerate
 * @return long The previously set framerate
 */
long kore::KoreApplication::setDesiredFramerate(long framerate)
{

	long last_framerate = m_DesiredFPS;
	m_DesiredFPS = framerate;
	m_DelayMs = 1000 / m_DesiredFPS;
	return last_framerate;

}

long kore::KoreApplication::getActualFramerate()
{

	return m_ActualFPS;

}

long kore::KoreApplication::getTicks()
{

	return SDL_GetTicks();

}

/**
 * Causes SDL to sleep for the number of milliseconds required by the
 * previous call to setDesiredFramerate()
 */
void kore::KoreApplication::sleep()
{

	long current_ms = SDL_GetTicks();
	assert(m_DelayMs > 0);

	if (m_LastSleepMs)
	{
		long delay_ms = m_DelayMs - (current_ms - m_LastSleepMs);
		if (delay_ms > 0) SDL_Delay(delay_ms);
		// printf("cur: %ld - last: %ld - el: %ld - del: %ld - Sleeping for %ld ms\n", current_ms, m_LastSleepMs, (current_ms - m_LastSleepMs), m_DelayMs, delay_ms);
	}

	// Save fps info
	m_ActualFPSCount++;
	if ((current_ms - m_ActualFPSTicks) >= 1000)
	{
		m_ActualFPS = m_ActualFPSCount;
		m_ActualFPSCount = 0;
		m_ActualFPSTicks = current_ms;
	}

	// Cache the value for the next call
	m_LastSleepMs = current_ms;

}

int kore::KoreApplication::run()
{
	long cx, cy;

	// Initiate message loop and initialize the needed subsystems
	SDL_Event ev;
	this->setDesiredFramerate(30);
	while (1)
	{

		while (SDL_PollEvent(&ev))
		{
			switch(ev.type)
			{
				case SDL_QUIT:
					printf("[application] Quitting...\n");
					return 0;

				case SDL_MOUSEMOTION:
					cx = ev.motion.x;
					cy = ev.motion.y;
					break;

			}
		}

		// Soft mouse cursor implementation. Should be moved to be
		// invoked once per frame.
		if (this->m_cursormode == kore::CM_SOFT)
		{
			kore::KoreContext* c = this->getPrimaryContext();
			kore::sdl::SdlSurface* s = new kore::sdl::SdlSurface(640,480);
			s->clear(0x000000FF);
			s->setPixel(cx, cy, 0xFFFFFFFF);
			s->setPixel(cx - 1, cy, 0xFFFFFFFF);
			s->setPixel(cx + 1, cy, 0xFFFFFFFF);
			s->setPixel(cx, cy - 1, 0xFFFFFFFF);
			s->setPixel(cx, cy + 1, 0xFFFFFFFF);
			s->blitRegion(SDL_GetVideoSurface(), 0, 0, 640, 480);
			delete s;
			c->refresh();
		}

		this->sleep();

	}
	return 0;
}

void kore::KoreApplication::setCursorMode(ECursorMode mode)
{
	// Either hide, show, or render the cursor.
	switch(mode)
	{
		case CM_HIDDEN:
			SDL_ShowCursor(SDL_DISABLE);
			this->m_cursormode = mode;
			return;
		case CM_HARD:
			SDL_ShowCursor(SDL_ENABLE);
			this->m_cursormode = mode;
			return;
		case CM_SOFT:
			SDL_ShowCursor(SDL_DISABLE);
			this->m_cursormode = mode;
			return;
	}
	
	printf("[application] Warning: setCursorMode called with invalid value\n");
}

/**
 * @brief Returns the current cursor mode
 *
 * The cursor mode is one of the CM_* constants.
 *
 * @return int The cursor mode currently active
 */
int kore::KoreApplication::getCursorMode()
{
	// Return cursor mode
	return(this->m_cursormode);
}

/**
 * Sets the primary context
 *
 * @important Make sure not to lose the handle to the context! The context is
 * hibernated automatically if it's attached (using detachPrimaryContext) but
 * only one context handle is being maintained. So make sure you save your
 * handles somewhere if you expect to use more than one single context.
 * 
 * @param kore::KoreContext* The context to assign as primary
 * @return int 0 if successful
 */
int kore::KoreApplication::setPrimaryContext(KoreContext* context)
{
	if (this->mp_primarycontext)
	{
		this->mp_primarycontext->detachPrimaryContext();
	}
	this->mp_primarycontext = context;
	this->mp_primarycontext->attachPrimaryContext();
}

/**
 * Returns the primary context (i.e. the context that is currently being
 * displayed).
 *
 * @return kore::KoreContext* The current primary context
 */
kore::KoreContext* kore::KoreApplication::getPrimaryContext()
{
	return this->mp_primarycontext;
}

